package com.company.nuwa.redis.wrapper;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.lang.Nullable;

import java.util.Collection;
import java.util.List;
import java.util.Objects;

import static java.util.stream.Collectors.toList;

/**
 * <p>description</p >
 *
 * @author Nikola Tesla
 * @version 1.0
 * @date 2020/10/29 15:44
 */
@Slf4j
public class RedisListOperationsWrapper {

    private final ListOperations<String, String> listOperations;

    public RedisListOperationsWrapper(StringRedisTemplate stringRedisTemplate) {
        this.listOperations = stringRedisTemplate.opsForList();
    }

    /**
     * Prepend {@code values} to {@code key}.
     *
     * @param key    must not be {@literal null}.
     * @param values must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="https://redis.io/commands/lpush">Redis Documentation: LPUSH</a>
     * @since 1.5
     */
    @Nullable
    public <T> Long rightPushAll(String key, Collection<T> values) {
        if (Objects.isNull(values)) {
            return null;
        }
        List<String> list = values.stream()
                .map(ValueConverter::getString)
                .collect(toList());
        return listOperations.rightPushAll(key, list);
    }

    @Nullable
    public <T> Long rightPush(String key, T value) {
        if (Objects.isNull(value)) {
            return null;
        }
        return listOperations.rightPush(key, ValueConverter.getString(value));
    }

    /**
     * Prepend {@code values} to {@code key}.
     *
     * @param key    must not be {@literal null}.
     * @param values must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="https://redis.io/commands/lpush">Redis Documentation: LPUSH</a>
     * @since 1.5
     */
    @Nullable
    public <T> Long leftPushAll(String key, Collection<T> values) {
        if (Objects.isNull(values)) {
            return null;
        }
        List<String> list = values.stream()
                .map(ValueConverter::getString)
                .collect(toList());
        return listOperations.leftPushAll(key, list);
    }

    @Nullable
    public <T> Long leftPush(String key, T value) {
        if (Objects.isNull(value)) {
            return null;
        }
        return listOperations.leftPush(key, ValueConverter.getString(value));
    }

    /**
     * Removes and returns last element in list stored at {@code key}.
     *
     * @param key       must not be {@literal null}.
     * @param classType class type
     * @return can be {@literal null}.
     * @see <a href="https://redis.io/commands/rpop">Redis Documentation: RPOP</a>
     */
    @Nullable
    public <T> T rightPop(String key, Class<T> classType) {
        String value = listOperations.rightPop(key);
        return ValueConverter.getBean(value, classType);
    }

    /**
     * 获取指定KEY所有List的元素
     *
     * @param key
     * @param classType
     * @param <T>
     * @return
     */
    public <T> List<T> getAll(String key, Class<T> classType) {
        List<String> range = listOperations.range(key, 0, -1);
        if (Objects.isNull(range)) {
            return Lists.newArrayList();
        }
        return range.stream()
                .map(e -> ValueConverter.getBean(e, classType))
                .collect(toList());
    }

    /**
     * Get elements between begin and end from list at key.
     * Params:
     * key – must not be null.
     * start –
     * end –
     * Returns:
     * null when used in pipeline / transaction.
     * See Also:
     * Redis Documentation: LRANGE
     */
    public <T> List<T> range(String key, long start, long end, Class<T> classType) {
        List<String> range = listOperations.range(key, start, end);
        if (Objects.isNull(range)) {
            return Lists.newArrayList();
        }
        return range.stream()
                .map(e -> ValueConverter.getBean(e, classType))
                .collect(toList());
    }

    /**
     * 删除指定KEY中，与T相同的所有元素
     *
     * @param key
     * @param count count> 0：删除等于从左到右移动的值的第一个元素；count< 0：删除等于从右到左移动的值的第一个元素；count = 0：删除等于value的所有元素
     * @param value
     * @param <T>
     * @return
     */
    public <T> Long remove(String key, long count, T value) {
        if (Objects.isNull(value)) {
            return null;
        }
        return listOperations.remove(key, count, ValueConverter.getString(value));
    }

}
